
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_ARITH.ALL;
USE IEEE.STD_LOGIC_SIGNED.ALL;
USE IEEE.NUMERIC_STD.ALL;

USE WORK.TYPES.ALL;

ENTITY Execute IS
    PORT(   Read_data_1     : IN     MEMDATA;
            Read_data_2     : IN     MEMDATA;
            Sign_extend     : IN     MEMDATA;
            Function_opcode : IN     STD_LOGIC_VECTOR( 5 DOWNTO 0 );
            ALUOp           : IN     STD_LOGIC_VECTOR( 1 DOWNTO 0 );
            ALUSrc          : IN     STD_LOGIC;
            Zero            : OUT    STD_LOGIC;
            ALU_Result      : OUT    MEMDATA;
            Add_Result      : OUT    MEMADDR;
            PC_plus_4       : IN     MEMADDR;
            clock, reset    : IN     STD_LOGIC
    );
END Execute;

ARCHITECTURE behavior OF Execute IS
SIGNAL Ainput, Binput 			: MEMDATA;
SIGNAL ALU_output_mux			: MEMDATA;
SIGNAL Branch_Add 			: MEMADDR;--was 8 DOWNTO 0
SIGNAL ALU_ctl				: STD_LOGIC_VECTOR( 2 DOWNTO 0 );
SIGNAL sig_shift                        : MEMDATA;
BEGIN
	Ainput <= Read_data_1 WHEN ALUSrc = '0'
                              ELSE (OTHERS => '0');
 
    -- ALU entrada do mux
    Binput <= Read_data_2   WHEN ALUSrc = '0'
                            ELSE Sign_extend;

	-- bits de controle da ALU
	ALU_ctl( 0 ) <= ( Function_opcode( 0 ) OR Function_opcode( 3 ) ) AND ALUOp(1 );
	ALU_ctl( 1 ) <= ( NOT Function_opcode( 2 ) ) OR (NOT ALUOp( 1 ) );
	ALU_ctl( 2 ) <= ( Function_opcode( 1 ) AND ALUOp( 1 )) OR ALUOp( 0 );

    -- Flag Zero
    Zero <= '1' WHEN ALU_output_mux = (ALU_output_mux'RANGE => '0')
                ELSE '0';   
 
    -- Seleciona saida da ALU
    ALU_result <= (0 => ALU_output_mux( ALU_output_mux'LEFT ), OTHERS => '0')   WHEN  ALU_ctl = "111" 
                                                                                ELSE  ALU_output_mux;

    -- Adiciona para incrementar o branch
    sig_shift(31 downto 2) <= Sign_extend(29 downto 0);
    sig_shift(1 downto 0) <= "00";
    Branch_Add    <= PC_plus_4 + sig_shift;
    Add_result    <= Branch_Add;

    PROCESS ( ALU_ctl, Ainput, Binput )
    BEGIN
        -- Selecao da operacao da ALU
        CASE ALU_ctl IS
            -- Operacao logica AND
            WHEN "000"     =>    ALU_output_mux     <= Ainput AND Binput; 
            -- Operacao logica OR
            WHEN "001"     =>    ALU_output_mux     <= Ainput OR Binput;
            -- ALUresult = A + B
            WHEN "010"     =>    ALU_output_mux     <= Ainput + Binput;
            -- ALU executa SLT
            WHEN "011"     =>    ALU_output_mux     <= Ainput - Binput;
            -- Operacao logica XOR
            WHEN "100"     =>    ALU_output_mux     <= Ainput XOR Binput;
            -- Operacao logica XOR
            WHEN "101"     =>    ALU_output_mux     <= Ainput NOR Binput;
            -- ALUresult = A_input -B_input
            WHEN "110"     =>    ALU_output_mux     <= Ainput - Binput;
            -- ALU executa SLT
            WHEN "111"     =>    ALU_output_mux     <= (OTHERS => '0') ;
            WHEN OTHERS    =>    ALU_output_mux     <= (OTHERS => '0') ;
        END CASE;
    END PROCESS;
END behavior;

